/*
 * Leds.c
 *
 *  Created on: Jun 8, 2010
 *      Author: iandrono
 */

#include "leds.h"
#include <avr/io.h>
#include <avr/iom644.h>
#include "../acelerometer/acelerometer.h"
#include <util/delay.h>
#include "leds.h"

//Variable de acelerometro
volatile extern booleanType holdedByAcelerometer;

typedef enum{ZERO, ONE} BIT_VALUES;

typedef struct {
	volatile uint8_t * CLK_DDR;
	volatile uint8_t * CLK_PORT;
	int CLK_PIN;
	volatile uint8_t * DATA_DDR;
	volatile uint8_t * DATA_PORT;
	int DATA_PIN;
	volatile uint8_t * CLR_DDR;
	volatile uint8_t * CLR_PORT;
	int CLR_PIN;
} latchType;

#define LEDPOWER_DDR &DDRB
#define LEDPOWER_PORT &PORTB
#define LEDPOWER_PIN PINB3
#define LATCHES_SIZE 16

latchType latches[COLORS_Q_MAX][LEDS_Q_MAX/LATCHES_SIZE];
unsigned char powersOf2[BYTE_SIZE] = {128,64,32,16,8,4,2,1};

void nop( int count );
void setBit( volatile uint8_t * port, int bit, BIT_VALUES value );
void initLatches();
void copyToVideoMemory( imageType * newImage );

void initLeds( void ) {
	int i,k;
	initLatches();

	for( k=0 ; k<COLORS_Q_MAX ; k++ ) {
		for( i=0 ; i<LEDS_Q_MAX/LATCHES_SIZE ; i++ ) {
			//Seteo todos los pins de los DDR como output
			setBit( latches[k][i].CLK_DDR, latches[k][i].CLK_PIN, ONE );
			setBit( latches[k][i].DATA_DDR, latches[k][i].DATA_PIN, ONE );
			setBit( latches[k][i].CLR_DDR, latches[k][i].CLR_PIN, ONE );
		}
	}

	//Puerto del 7805 con enable
	setBit( LEDPOWER_DDR, LEDPOWER_PIN, ONE );
}

void initLatches() {
	//Esto se tiene que setear a mano
	latches[RED][0].CLK_DDR = &DDRB;
	latches[RED][0].CLK_PORT = &PORTB;
	latches[RED][0].CLK_PIN = PINB1;
	latches[RED][0].DATA_DDR = &DDRB;
	latches[RED][0].DATA_PORT = &PORTB;
	latches[RED][0].DATA_PIN = PINB6;
	latches[RED][0].CLR_DDR = &DDRB;
	latches[RED][0].CLR_PORT = &PORTB;
	latches[RED][0].CLR_PIN = PINB0;

	latches[GREEN][0].CLK_DDR = &DDRB;
	latches[GREEN][0].CLK_PORT = &PORTB;
	latches[GREEN][0].CLK_PIN = PINB1;
	latches[GREEN][0].DATA_DDR = &DDRB;
	latches[GREEN][0].DATA_PORT = &PORTB;
	latches[GREEN][0].DATA_PIN = PINB4;
	latches[GREEN][0].CLR_DDR = &DDRB;
	latches[GREEN][0].CLR_PORT = &PORTB;
	latches[GREEN][0].CLR_PIN = PINB0;

	latches[BLUE][0].CLK_DDR = &DDRB;
	latches[BLUE][0].CLK_PORT = &PORTB;
	latches[BLUE][0].CLK_PIN = PINB1;
	latches[BLUE][0].DATA_DDR = &DDRB;
	latches[BLUE][0].DATA_PORT = &PORTB;
	latches[BLUE][0].DATA_PIN = PINB2;
	latches[BLUE][0].CLR_DDR = &DDRB;
	latches[BLUE][0].CLR_PORT = &PORTB;
	latches[BLUE][0].CLR_PIN = PINB0;

}

void updateLeds() {
	int i,j,k,x;
	int nopDelays = 0;
	unsigned char toShowR, toShowG, toShowB;
	unsigned char bitAuxR, bitAuxG, bitAuxB;

	if( !holdedByAcelerometer ) {
		//K=0 ya que CLK y CLR son el mismo para todos los latches!
		k = 0;
		for( j=0 ; j<LEDS_Q_MAX/LATCHES_SIZE ; j++ ) {
			//Apago el 7805 de las luces
			setBit( LEDPOWER_PORT, LEDPOWER_PIN, ONE );
			//_delay_ms(100);

			//Subo el CLR
			setBit( latches[k][j].CLR_PORT, latches[k][j].CLR_PIN, ONE );

			for( x=0 ; x<LATCHES_SIZE/BYTE_SIZE ; x++ ) {
				//j += x;
				toShowR = ~(videoMemory[RED][videoMemoryIndex][j+x]);
				toShowG = ~(videoMemory[GREEN][videoMemoryIndex][j+x]);
				toShowB = ~(videoMemory[BLUE][videoMemoryIndex][j+x]);

				for ( i=0 ; i<BYTE_SIZE ; i++ ) {
					//Le doy un time al latch
					nop(nopDelays);

					//Prendo el clock
					setBit( latches[k][j].CLK_PORT, latches[k][j].CLK_PIN, ZERO );

					//Le doy un time al latch
					nop(nopDelays);

					//Saco los 3 bits a mostrar para cada color
					bitAuxR = toShowR&powersOf2[i];
					bitAuxG = toShowB&powersOf2[i];
					bitAuxB = toShowG&powersOf2[i];
					//Meto los 3 bit cada uno a su color
					setBit( latches[RED][j].DATA_PORT, latches[RED][j].DATA_PIN, (bitAuxR==0?ZERO:ONE) );
					setBit( latches[GREEN][j].DATA_PORT, latches[GREEN][j].DATA_PIN, (bitAuxG==0?ZERO:ONE) );
					setBit( latches[BLUE][j].DATA_PORT, latches[BLUE][j].DATA_PIN, (bitAuxB==0?ZERO:ONE) );

					//Le doy un time al latch
					nop(nopDelays);

					//Apago el clock
					setBit( latches[k][j].CLK_PORT, latches[k][j].CLK_PIN, ONE );
				}
			}
			//_delay_ms(100);
			//Prendo el 7805 de las luces
			setBit( LEDPOWER_PORT, LEDPOWER_PIN, ZERO );
		}

		videoMemoryIndex += videoMemoryStep;

		if( videoMemoryIndex == ROWS_Q ) {
			videoMemoryStep = DOWN;
			videoMemoryIndex--;
		} else if( videoMemoryIndex == -1 ) {
			videoMemoryStep = UP;
			videoMemoryIndex++;
		}

	} else {
		//Subo el CLR para apagar todas las luces
		setBit( latches[RED][0].CLR_PORT, latches[RED][0].CLR_PIN, ONE );
	}
}

void copyToVideoMemory( imageType * newImage ) {
	int i,j,k;

	for( i=0 ; i<COLORS_Q_MAX ; i++ ) {
		for( j=0 ; j<ROWS_Q ; j++ ) {
			for( k=0 ; k<LEDS_Q_MAX/BYTE_SIZE ; k++ ) {
				videoMemory[i][j][k] = (*newImage)[i][j][k];
			}
		}
	}
}

void resetVideoMemory( imageType * newImage ) {
	copyToVideoMemory(newImage);
	videoMemoryIndex = 0;
	videoMemoryStep = UP;
}

void nop( int count ) {
	int aux = 0;
	while(aux++ < count);
}

void setBit( volatile uint8_t * port, int bit, BIT_VALUES value ) {
	switch( value ) {
		case ZERO:
			(*port) &= (~powersOf2[BYTE_SIZE-bit-1]);
			break;
		case ONE:
			(*port) |= (1<<bit);
			break;
	}
}

void startSequence( void ) {
	setBit( LEDPOWER_PORT, LEDPOWER_PIN, ZERO );
	_delay_ms(500);
	setBit( LEDPOWER_PORT, LEDPOWER_PIN, ONE );
	_delay_ms(500);
	setBit( LEDPOWER_PORT, LEDPOWER_PIN, ZERO );
	_delay_ms(500);
	setBit( LEDPOWER_PORT, LEDPOWER_PIN, ONE );
	_delay_ms(500);
}
